package org.myspringframework.beans.factory.support;

import com.sun.org.apache.bcel.internal.generic.IF_ACMPEQ;
import org.myspringframework.beans.BeansException;
import org.myspringframework.beans.factory.BeanFactory;
import org.myspringframework.beans.factory.aware.Aware;
import org.myspringframework.beans.factory.aware.BeanClassLoaderAware;
import org.myspringframework.beans.factory.aware.BeanFactoryAware;
import org.myspringframework.beans.factory.aware.BeanNameAware;
import org.myspringframework.beans.factory.config.DisposableBean;
import org.myspringframework.beans.factory.config.DisposableBeanAdapter;
import org.myspringframework.beans.factory.config.InitializingBean;
import org.myspringframework.beans.factory.config.beandefinition.PropertyValue;
import org.myspringframework.beans.factory.config.beandefinition.PropertyValues;
import org.myspringframework.beans.factory.AutowireCapableBeanFactory;
import org.myspringframework.beans.factory.config.beandefinition.BeanDefinition;
import org.myspringframework.beans.factory.config.postprocessor.BeanPostProcessor;
import org.myspringframework.beans.factory.config.beandefinition.BeanReference;
import org.myspringframework.beans.factory.config.postprocessor.InstantiationAwareBeanPostProcessor;
import org.myspringframework.beans.factory.config.postprocessor.SmartInstantiationAwareBeanPostProcessor;
import org.myspringframework.beans.factory.support.strategy.CglibSubclassingInstantiationStrategy;
import org.myspringframework.beans.factory.support.strategy.InstantiationStrategy;
import org.myspringframework.beans.factory.support.strategy.SimpleInstantiationStrategy;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;

//在这里进行bean的创建(实例化->依赖注入(循环依赖时提前生成AOP代理)->beanpostprocessor:Befort->初始化->beanpostprocessor:After(AOP))
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

    //使用策略模式决定实例化方法
    private InstantiationStrategy instantiationStrategy = new SimpleInstantiationStrategy();

    //创建Bean
    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition,Object[] args) throws BeansException {

            //  InstantiationAwareBeanPostProcessor在创建 bean 实例之前提供一个扩展点，目标对象实例化之前调用，该方法返回值类型是Object，也就是说可以返回任何类型的值，此时我们可以返回原本目标的代理对象，或者返回任意的其他对象。
            //  如果该方法返回值代替了原本该生成的目标对象，那么后续的很多流程都会跳过，只执行postProcessAfterInitialization方法。
            Object bean = resolveBeforeInstantiation(beanName, beanDefinition);
            if (null != bean) {
                //直接返回，跳过依赖注入和初始化，不会添加到单例bean集合
                return bean;
            }

            //1.实例化Object
            try{
                bean =createBeanInstance(beanDefinition,beanName,args);
            } catch (InvocationTargetException | NoSuchMethodException | InstantiationException | IllegalAccessException e) {
                throw new BeansException("实例化bean失败", e);
            }

            if (beanDefinition.isSingleton()&&this.isSingletonCurrentlyInCreation(beanName)){
                Object theBean = bean;
                //填充属性之前，会将当前正在创建的Bean的object工厂放入到singletonFactories中
                this.addSingletonFactory(beanName, () -> {
                    //获取提前暴露的对象
                    return this.getEarlyBeanReference(beanName, beanDefinition, theBean);
                });
            }

            // 在设置 Bean 属性之前，允许 BeanPostProcessor 修改属性值
            applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition);

            //2.属性注入
            applyPropertyValues(beanName,bean,beanDefinition);

            //3.执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法
            Object exposedObject=bean;
            exposedObject = initializeBean(beanName, bean, beanDefinition);

            //如果出现循环依赖，进行过提前生成aop代理，则二级缓存中的代理对象为最终需要的bean
            if (beanDefinition.isSingleton()&&this.isSingletonCurrentlyInCreation(beanName)){
                Object earlySingletonReference = this.getSingleton(beanName, false);
                if (earlySingletonReference!=null){
                    if (exposedObject==bean){
                        exposedObject=earlySingletonReference;
                    }
                }
            }


        // 注册实现了 DisposableBean 接口的 Bean 对象
        registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);

        return exposedObject;
    }

    // 在创建 bean 实例之前提供一个扩展点，允许开发者在实例化 bean 之前对其进行定制，如返回代理对象
    protected Object resolveBeforeInstantiation(String beanName, BeanDefinition beanDefinition) {
        //执行 InstantiationAwareBeanPostProcessor的BeforeInstantiation()方法
        Object bean = applyBeanPostProcessorsBeforeInstantiation(beanDefinition.getBeanClass(), beanName);
        if (null != bean) {
            //再执行beanPostProcessor的after方法
            bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
        }
        return bean;
    }

    //执行 InstantiationAwareBeanPostProcessor的BeforeInstantiation()方法
    protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
        for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
            if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
                Object result = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessBeforeInstantiation(beanClass, beanName);
                if (null != result) return result;
            }
        }
        return null;
    }


    //实例化Object（通过策略方式选择实例化方式（jdk-反射;cjlib-字节码框架ASM实现）），通过args的个数来选择构造方法)
    private Object createBeanInstance( BeanDefinition beanDefinition,String beanName, Object[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Constructor constructor=null;
        Constructor[] declaredConstructors = beanDefinition.getBeanClass().getDeclaredConstructors();
        //遍历构造函数,选择与参数匹配的构造函数
        for (Constructor declaredConstructor : declaredConstructors) {
            int parameterCount = declaredConstructor.getParameterCount();
            /**todo:推断构造方法这里只进行个数匹配,实际上应该考虑更多 还要考虑是否加了@Autpwried

             * Spring中的一个bean，需要实例化得到一个对象，而实例化就需要用到构造方法， 一个类中的构造方法一般分为两种情况：
             *
             * 1.只有一个构造方法
             *
             * 只有一个无参的构造方法：如果只有一个无参的构造方法，那么实例化就只能使用这个无参的构造方法；
             *
             * 只有一个有参的构造方法：如果只有一个有参的构造方法，那么实例化时能使用这个构造方法吗？要分情况：
             *
             * ① 使用AnnotationConfigApplicationContext，会使用这个构造方法进行实例化，Spring会根据构造方法的参数信息去找bean，然后传给构造方法；
             *
             * ② 使用ClassPathXmlApplicationContext，表示使用XML的方式来注册bean，在XML中指定构造方法的参数值(手动指定)，或者配置autowire=constructor让Spring自动去找bean作为构造方法参数值。
             *
             * 2.有多个构造方法
             *
             * 上面是只有一个构造方法的情况，如果有多个构造方法呢？还得看多个构造方法中有没有无参的构造方法，一个类存在多个构造方法，那么Spring进行实例化之前，该如何去确定到底用哪个构造方法呢？ 具体分析如下：
             *
             * 2.1. 如果开发者指定了想要使用的构造方法，那就用指定的构造方法，指定构造方法的方式：
             *
             * ① xml中的<constructor-arg>标签，这个标签表示构造方法参数，所以可以根据这个确定想要使用的构造方法的参数个数，从而确定想要使用的构造方法；
             *
             * ② 通过@Autowired注解，@Autowired注解可以写在构造方法上(构造方法注入)，所以哪个构造方法上写了 @Autowired注解，表示开发者想使用哪个构造方法，但是，它和第一种方式有所不同，通过 xml的方式，我们直接指定了构造方法的参数值，而通过@Autowired注解的方式，只是告诉Spring我们想要用的那个构造方法，但是需要Spring 通过byType+byName的方式去找到符合条件的bean作为构造方法的参数值；
             *
             * ③ 通过getBean()或者beanDefinition.getConstructorArgumentValues()指定,使用getBean()来指定要使用的构造方法，该bean必须是懒加载的或者原型的才会生效；
             */
            if (args!=null&&parameterCount== args.length){
                constructor=declaredConstructor;
                break;
            }
        }
        //通过策略方式选择实例化方式（jdk-反射;cjlib-字节码框架ASM实现
        return instantiationStrategy.instantiate(beanDefinition,beanName,constructor,args);
    }

    //获取提前暴露的对象(提前生成aop代理！)
    protected Object getEarlyBeanReference(String beanName, BeanDefinition beanDefinition, Object bean) {
        Object exposedObject = bean;
        List<BeanPostProcessor> beanPostProcessors = getBeanPostProcessors();
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            //遍历所有SmartInstantiationAwareBeanPostProcessor
            if (beanPostProcessor instanceof SmartInstantiationAwareBeanPostProcessor){
                SmartInstantiationAwareBeanPostProcessor ibp=(SmartInstantiationAwareBeanPostProcessor)beanPostProcessor;
                //获取提前暴露的对象
                exposedObject = ibp.getEarlyBeanReference(bean, beanName);
            }
        }

        return exposedObject;
    }

    /**
     * 在设置 Bean 属性之前，允许 BeanPostProcessor 修改属性值
     */
    protected void applyBeanPostProcessorsBeforeApplyingPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
        for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
            if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor){
                PropertyValues pvs = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessPropertyValues(beanDefinition.getPropertyValues(), bean, beanName);
                if (null != pvs) {
                    for (PropertyValue propertyValue : pvs.getPropertyValues()) {
                        beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
                    }
                }
            }
        }
    }

    //属性注入
    private void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
        try{
            //从beanDefinition获取属性值集合
            PropertyValues propertyValues = beanDefinition.getPropertyValues();
            if (propertyValues==null){
                //没有属性
                return;
            }
            for(PropertyValue propertyValue : propertyValues.getPropertyValueList()){
                String name = propertyValue.getName();
                Object value = propertyValue.getValue();

                if (value instanceof BeanReference) {
                    // A 依赖 B，获取 B 的实例化
                    BeanReference beanReference = (BeanReference) value;
                    value = getBean(beanReference.getBeanName());
                }
                //反射为属性赋值
                Class beanClass = beanDefinition.getBeanClass();
                Field declaredField = beanClass.getDeclaredField(name);
                declaredField.setAccessible(true);
                //根据字段类型匹配！
                //todo:目前支支持int double boolean
                Class<?> fieldType = declaredField.getType();
                if (fieldType==int.class||fieldType==Integer.class){
                    declaredField.setInt(bean,Integer.parseInt((String)value));
                }else if (fieldType== Double.class||fieldType== double.class){
                    declaredField.setDouble(bean,Double.valueOf((String) value));
                }else if (fieldType==Boolean.class||fieldType==boolean.class){
                    declaredField.setBoolean(bean,Boolean.getBoolean((String) value));
                }else {
                    //为引用类型
                    declaredField.set(bean,value);
                }
            }
        }catch (Exception e){
            e.printStackTrace();
            throw new BeansException("Error setting property values：" + beanName);

        }
    }

    public InstantiationStrategy getInstantiationStrategy() {
        return instantiationStrategy;
    }

    public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
        this.instantiationStrategy = instantiationStrategy;
    }

    //执行bean的初始化,注意这里需要放回初始化后的对象，
    private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {

        //1.对实现aware接口的bean进行设值
        invokeAwareMethod(beanName, bean);

        // 2. 执行 BeanPostProcessor Before 处理
        Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);

        try {
            // 3.执行 Bean 对象的初始化方法
            invokeInitMethods(beanName, wrappedBean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("调用初始化方法失败！" + beanName , e);
        }

        // 4. 执行 BeanPostProcessor After 处理
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        return wrappedBean;
    }

    //对实现aware接口的bean进行设值(感知)
    private void invokeAwareMethod(String beanName, Object bean) {
        if (bean instanceof Aware) {
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(this.getBeanFactory());
            }
            if (bean instanceof BeanClassLoaderAware){
                ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
            }
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
        }
    }

    //执行初始化方法
    private void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Exception {
        //判断是否继承了接口 InitializingBean
        if (bean instanceof InitializingBean){
            //执行afterPropertiesSet
            ((InitializingBean) bean).afterPropertiesSet();
        }

        //判断配置文件中是否有配置init-method
        String initMethodName = beanDefinition.getInitMethodName();
        if (initMethodName!=null&&initMethodName!=""){
            Method method = beanDefinition.getBeanClass().getMethod(initMethodName);
            if (method==null){
                throw new BeansException("在"+beanName+"中找不到初始化方法:"+initMethodName);
            }else {
                //反射调用初始化方法
                method.invoke(bean);
            }
        }
    }

    //调用BeanPostProcessor的前置方法
    @Override
    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        //遍历BeanPostProcessor
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            //执行前置方法
            Object current = processor.postProcessBeforeInitialization(result, beanName);
            if (null == current) return result;
            result = current;
        }
        return result;
    }

    //调用BeanPostProcessor的后置方法
    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        List<BeanPostProcessor> beanPostProcessors = getBeanPostProcessors();
        for (BeanPostProcessor processor : beanPostProcessors) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (null == current) continue;
            result = current;
        }
        return result;
    }

    //注册DisposableBean
    protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {

        //非单例bean不用销毁
        if (!beanDefinition.isSingleton()){
            return;
        }

        String destroyMethodName = beanDefinition.getDestroyMethodName();
        if (bean instanceof DisposableBean || destroyMethodName!=null) {
            //注册DisposableBean
            registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
        }
    }

    protected BeanFactory getBeanFactory(){
        return this;
    }
}